package com.onpositive.wikistorage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class LinkStorage {
	
	private static final String ATTRIBUTE_NAME_CASE = "case";
	private static final String SUBELEMENT_NAME_CASE_MAPPING = "case-mapping";
	private static final String SUBELEMENT_NAME_MAPPING = "mapping";
	private static final String ATTRIBUTE_NAME_COVALUE = "co-value";
	private static final String ATTRIBUTE_NAME_VALUE = "value";
	private static final String SUBELEMENT_NAME_VALUE = "val";
	private static final String SUBELEMENT_PROPERTY = "prop";
	private static final String ATTRIBUTE_NAME_PROPERTY_NAME = "prop-name";
	private static final String ATTRIBUTE_NAME_TITLE = "title";

	public LinkStorage(String title) {
		super();
		this.title = title;
	}
	
	public LinkStorage( Element element ) {
		super();
		this.title = element.getAttribute(ATTRIBUTE_NAME_TITLE) ;
		init(element) ;
	}

	final String title ;
	HashMap<String,HashMap<String,String>> perPropertyStorage = new HashMap<String, HashMap<String,String>>() ;
	HashMap<String,String> caseResolver = new HashMap<String,String>() ;
	
	public String getTitle() {
		return title;
	}

	public String getBestCovalue( String propName, String value ){
		HashMap<String,String> propMap = perPropertyStorage.get(propName) ;
		if( propMap == null )
			return null ;
		
		String key = canonizeKeyString(value) ;
		return propMap.get(key) ;		
	}
	
	public String getBestCase( String value ){
		String key = canonizeKeyString(value) ;
		return caseResolver.get(key) ;
	}
	
	public HashMap<String,String> getPropertyMapForCompletion( String propName ){
		
		if( propName == null )
			return null;
		
		HashMap<String,String> propRelatedMap = perPropertyStorage.get(propName) ;
		if( propRelatedMap == null ){
			propRelatedMap = new HashMap<String, String>() ;
			perPropertyStorage.put(propName, propRelatedMap) ;
		}
		return propRelatedMap ;		
	}
	
	public HashMap<String,String> getPropertyMap( String propName ){
		
		if( propName == null )
			return null;
		
		return perPropertyStorage.get(propName) ;
	}
	
	public HashMap<String, String> getCaseResolver() {
		return caseResolver;
	}
	public void setFirstLetterToUpperCase(){
		
		ArrayList<String> lst = new ArrayList<String>( caseResolver.values() ) ;
		StringBuilder bld = new StringBuilder() ;
		for( String str : lst ){
			String key = canonizeKeyString(str) ;
			
			bld.delete(0, bld.length()) ;
			bld.append( Character.toUpperCase( str.charAt(0)) ) ;
			bld.append( str.substring(1)) ;
			
			caseResolver.put(key, bld.toString() ) ;			
		}		
	}
	
	public HashSet<String> fullCollection() {
	
		HashSet<String> result = new HashSet<String>() ;
		
		for( HashMap<String,String> map : perPropertyStorage.values() )
			result.addAll( map.keySet() ) ;
		
		return result;
	}
	
	private void init(Element element)
	{
		
		NodeList mappingsList = element.getElementsByTagName( SUBELEMENT_NAME_MAPPING ) ;
		Element mappingElement = null ;
		int l = mappingsList.getLength() ;
		for( int i = 0 ; i < l ; i++  ){
			Node n = mappingsList.item(i);
			if (n instanceof Element) {
				mappingElement = (Element) n;
				break ;
			}			
		}
		
		NodeList caseMappingsList = element.getElementsByTagName( SUBELEMENT_NAME_CASE_MAPPING ) ;
		Element caseMappingElement = null ;
		l = caseMappingsList.getLength() ;
		for( int i = 0 ; i < l ; i++  ){
			Node n = caseMappingsList.item(i);
			if (n instanceof Element) {
				caseMappingElement = (Element) n;
				break ;
			}			
		}
		
		NodeList children ;
		if( caseMappingElement != null ){
			children = caseMappingElement.getChildNodes();
			l = children.getLength();
	
			for (int i = 0; i < l; i++) {
				Node n = children.item(i);
				if (n instanceof Element) {
					Element valueElement = (Element) n;
					
					String value = valueElement.getAttribute(ATTRIBUTE_NAME_VALUE);
					
					if( value.length() == 0 )
						continue ;
					
					String coValue = valueElement.getAttribute(ATTRIBUTE_NAME_CASE);
					if( coValue.length() == 0 )
						continue ;
					
					caseResolver.put(value, coValue) ;				
				}
			}
		}
		
		children = mappingElement.getChildNodes();		
		l = children.getLength();

		for (int i = 0; i < l; i++) {
			Node n = children.item(i);
			if (n instanceof Element) {
				Element propElement = (Element) n;
				String propName = propElement.getAttribute(ATTRIBUTE_NAME_PROPERTY_NAME);
				if(propName.length() == 0)
					continue ;
				
				HashMap<String,String> propMap = new HashMap<String, String>() ;
				
				NodeList propChildren = element.getChildNodes();
				int l0 = propChildren.getLength();
				for (int j = 0; j < l0; j++) {
					Node cNode = propChildren.item(j);
					
					if (cNode instanceof Element) {
						Element valueElement = (Element) n;
						String value = valueElement.getAttribute(ATTRIBUTE_NAME_VALUE);
						
						if( value.length() == 0 )
							continue ;
						
						String coValue = valueElement.getAttribute(ATTRIBUTE_NAME_COVALUE);
						if( coValue.length() == 0 )
							coValue = null ;
						
						propMap.put(value, coValue) ;
					}
				}
				
				if( propMap.size() != 0 )
					perPropertyStorage.put(propName, propMap) ;
			}
		}
		
	}
	
	public void writeValues( Element element )
	{
		element.setAttribute(ATTRIBUTE_NAME_TITLE, title) ;
		Document doc = element.getOwnerDocument() ;
		
		Element mappingElement = doc.createElement(SUBELEMENT_NAME_MAPPING) ;
		element.appendChild(mappingElement) ;
		
		for( Map.Entry<String, HashMap<String,String>> entry : perPropertyStorage.entrySet() )
		{
			String propName = entry.getKey() ;
			HashMap<String,String> propMap = entry.getValue() ;
			
			Element propElement = doc.createElement(SUBELEMENT_PROPERTY) ;
			mappingElement.appendChild(propElement) ;
			propElement.setAttribute( ATTRIBUTE_NAME_PROPERTY_NAME, propName ) ;
			
			for( Map.Entry<String,String> e : propMap.entrySet() ){
				
				String value = e.getKey() ;
				String coValue = e.getValue() ;
				
				Element valElement = doc.createElement(SUBELEMENT_NAME_VALUE) ;
				propElement.appendChild(valElement) ;
				
				valElement.setAttribute( ATTRIBUTE_NAME_VALUE, value ) ;				
				if(coValue != null )
					valElement.setAttribute( ATTRIBUTE_NAME_COVALUE, coValue ) ;								
			}			
		}
		
		Element caseElement = doc.createElement(SUBELEMENT_NAME_CASE_MAPPING) ;
		element.appendChild(caseElement) ;
		
		for( Map.Entry<String,String> entry : caseResolver.entrySet() )
		{
			String key = entry.getKey() ;
			String val = entry.getValue() ;
			
			Element valElement = doc.createElement(SUBELEMENT_NAME_VALUE) ;
			caseElement.appendChild(valElement) ;
			
			valElement.setAttribute( ATTRIBUTE_NAME_VALUE, key ) ;				
			valElement.setAttribute( ATTRIBUTE_NAME_CASE, val ) ;
		}
	}

	public static String canonizeKeyString(String value)
	{
		if( value == null )
			return null ;
		
		return value.toLowerCase().replace('_', ' ') ;
	}
	
}
